package map.spawnStation;

import gameStates.absGamesStates.AbsIngameState;

import interfaces.spawnMenu.spawnMap.SpawnButtonContent.SpawnType;

import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;

import networking.messages.syncMessages.ValueType;

import logic.SpawnLocation;
import logic.common.player.Player;
import logic.common.player.playerListeners.NearStationChangedEvent;
import logic.common.player.playerListeners.PlayerStationListener;
import logic.common.player.playerListeners.SelectedSpawnChangedEvent;
import logic.common.team.Team;
import logic.nodes.TeamNode;
import logic.nodes.collision.visibility.SightType;
import logic.status.SpawnValueManager;
import logic.status.StatusValueManager;
import logic.weapons.WeaponFire;

import com.jme.scene.Node;

import main.InitGame;
import main.listeners.ListenerContainer;
import map.spawnStation.stationListeners.ConquerHelpedEvent;
import map.spawnStation.stationListeners.SpawnStationListener;
import map.spawnStation.stationListeners.SpawnStationPointsListener;
import map.spawnStation.stationListeners.StationConqueredEvent;

public class SpawnStation extends TeamNode implements SpawnPosition {
	
	private static final long serialVersionUID = 1L;
	
	private SpawnPoint spawnPoint;
	private Team currentTeam, oldTeam;
	private ListenerContainer listeners;
	private HashMap<Team, LinkedList<Player>> conquerers;
	private HashMap<Team, HashSet<Player>> conquerersSet;
	private HashMap<Player, PlayerStationListener> playerStationListeners;
	private SpawnValueManager spawnValueManager;
	private Player lastCapturingPlayer;
	
	public SpawnStation(String modelPath, String title, Node model, SpawnPointProperties pointProps, 
						SpawnStationProperties stationProps, AbsIngameState ingameState) {
		super(modelPath, title, model, stationProps, null, ingameState);
		spawnPoint = new SpawnPoint(pointProps);
		getLocalTranslation().set(spawnPoint.getLocation());
		setID(spawnPoint.getID());
		updateRenderState();
		
		listeners = new ListenerContainer(SpawnStationListener.class);
		
		spawnValueManager = new SpawnValueManager(this);
		spawnValueManager.changeValue(ValueType.SpawnPoints, 0f);
		
		playerStationListeners = new HashMap<Player, PlayerStationListener>();
		
		conquerers = new HashMap<Team, LinkedList<Player>>();
		conquerersSet = new HashMap<Team, HashSet<Player>>();
		
		InitGame.get().getNetworkState().registerStaticObject(this);
		
		angles.initStationLetter();
	}
	
	public void addSpawnStationPointsListener(SpawnStationPointsListener l) {
		spawnValueManager.addListener(l);
	}
	
	public void addSpawnStationPointsListener(SpawnStationPointsListener l, float diff) {
		spawnValueManager.addListener(l, diff);
	}
	
	public void removeSpawnStationPointsListener(SpawnStationPointsListener l) {
		spawnValueManager.removeListener(l);
	}
	
	public void addSpawnStationListener(SpawnStationListener l) { listeners.addListener(l); }
	
	public void removeSpawnStationListener(SpawnStationListener l) { listeners.removeListener(l); }
	
	@Override
	public float getActionRadius() { return spawnPoint.getRadius(); }
	
	@Override
	public SpawnLocation getSpawnLocation() { return spawnPoint; }
	
	@Override
	public void setTeam(Team newTeam) {
		if(team == newTeam) return;
		if(team != null) team.removeSpawnStation(this);
		
		oldTeam = team;
		super.setTeam(newTeam);
		
		if(team == null) {
			listeners.callMethod(SpawnStationListener.STATIONNEUTRALIZED);
			return;
		}
		
		team.addSpawnStation(this);
		
		if(conquerers != null) {
			Player conquerer = conquerers.get(team).poll();
			if(conquerer != null) {
				assert(!conquerers.containsKey(conquerer));
				StationConqueredEvent cEvent = new StationConqueredEvent(this, conquerer);
				listeners.callMethod(SpawnStationListener.CONQUERED, cEvent);
				
				for(Player p : conquerers.get(team)) {
					ConquerHelpedEvent hEvent = new ConquerHelpedEvent(this, p);
					listeners.callMethod(SpawnStationListener.HELPEDCONQUER, hEvent);
				}
				
				conquerers.get(team).clear();
				conquerersSet.get(team).clear();
			}
		}
		
		updateVisibility(true, team);
	}
	
	public void changeCapturePoints(float amount, final Player capturingPlayer) {
		final Team team = capturingPlayer.getTeam();
		if(this.team == team && getCapturePoints() == getMaxCapturePoints()) return;
		
		addCapturer(capturingPlayer);
		
		if(!playerStationListeners.containsKey(capturingPlayer)) {
			PlayerStationListener l = new PlayerStationListener() {
				@Override
				public void nearSpawnStationChanged(NearStationChangedEvent event) {
					if(event.getSpawnPos() != SpawnStation.this) {
						conquerersSet.get(team).remove(capturingPlayer);
						conquerers.get(team).remove(capturingPlayer);
					}
				}
				
				@Override
				public void selectedSpawnPositionChanged(SelectedSpawnChangedEvent event) {}
			};
			capturingPlayer.addPlayerStationListener(l);
		}
		
		changeCapturePoints(amount, capturingPlayer.getTeam());
	}
	
	public void addCapturer(Player capturingPlayer) {
		lastCapturingPlayer = capturingPlayer;
		
		Team team = capturingPlayer.getTeam();
		if(!conquerersSet.get(team).contains(capturingPlayer)) {
			conquerersSet.get(team).add(capturingPlayer);
			conquerers.get(team).add(capturingPlayer);
		}
	}
	
	private void changeCapturePoints(float amount, Team capturingTeam) {
		float capturePoints = getCapturePoints();
		if(currentTeam == null) {
			currentTeam = capturingTeam;
			capturePoints += amount;
		} else if(currentTeam == capturingTeam) {
			capturePoints += amount;
		} else {
			if(capturePoints == 0) {
				currentTeam = capturingTeam;
				capturePoints += amount;
			} else {
				capturePoints -= amount;
				if(capturePoints < 0) {
					capturePoints = 0;
					currentTeam = null;
				}
			}
		}
		
		spawnValueManager.changeValue(ValueType.SpawnPoints, capturePoints);
		
		if(currentTeam == null) setTeam(null);
		else {
			int maxValue = getMaxCapturePoints();
			if(capturePoints >= maxValue) {
				capturePoints = maxValue;
				setTeam(currentTeam);
			}
		}
	}
	
	public int getMaxCapturePoints() { return ((SpawnPoint)getSpawnLocation()).getMaxCapturePoints(); }
	
	public float getCapturePoints() { return spawnValueManager.getFloatValue(ValueType.SpawnPoints); }
	
	public Team getCurrentTeam() { return currentTeam; }
		
	@Override
	public float getDefaultScale() { return 1f; }
	
	@Override
	public boolean canMove() {	return false; }
	
	@Override
	public int getDestroyMoney() { return 300; }
	
	public void setAvailableTeams(Team teamA, Team teamB) {
		conquerers.put(teamA, new LinkedList<Player>());
		conquerers.put(teamB, new LinkedList<Player>());
		
		conquerersSet.put(teamA, new HashSet<Player>());
		conquerersSet.put(teamB, new HashSet<Player>());
	}
	
	public Player getLastCapturingPlayer() { return lastCapturingPlayer; }
	
	public String getStationLetter() { return spawnPoint.getStationLetter(); }
	
	public Team getOldTeam() { return oldTeam; }
	
	@Override
	public void receiveDamage(int damage) {}
	
	@Override
	public void receiveDamage(WeaponFire weaponFire) {}
	
	@Override
	public SpawnType getSpawnType() { return SpawnType.SpawnStation; }
	
	@Override
	public SightType getSightType() { return SightType.SpawnStation; }
	
	@Override
	public ListenerContainer getListenerContainer(Class<?> listenerClass) {
		if(listenerClass == SpawnStationListener.class) return listeners;
		if(listenerClass == SpawnStationPointsListener.class) return spawnValueManager.getDefaultContainer().getListenerContainer();
		return super.getListenerContainer(listenerClass);
	}
	
	@Override
	public StatusValueManager<?> getListenerContainer(Class<?> listenerClass, float diff) {
		if(listenerClass == SpawnStationPointsListener.class) return spawnValueManager;
		return super.getListenerContainer(listenerClass, diff);
	}
}